#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cerrno>
#include <netdb.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctime>

#include <nlohmann/json.hpp>

#include "monitor.h"

{% for service in services %}
#include "../../../atomic_service/{{service["grpc_info"]["name"]}}/sync_server_impl/{{ service["grpc_info"]["sync_server_impl"] }}.cpp"
{% endfor %}
using json = nlohmann::json;

{% for service in services %}
class {{ service["grpc_info"]["name_service"] }}_ServerImpl ;
{% endfor%}


void RunServer(const std::string& ip, const std::string& port) {
    std::string server_address(ip+":"+port);
    ::grpc::ServerBuilder builder;
    builder.AddListeningPort(server_address, ::grpc::InsecureServerCredentials());
    // TODO add service
    {% for service in services %}
    {{ service["grpc_info"]["name_service"] }}_ServerImpl {{ service["grpc_info"]["name"] }}_instance;
    builder.RegisterService(&{{ service["grpc_info"]["name"] }}_instance);
    {% endfor %}
    std::unique_ptr<::grpc::Server> server(builder.BuildAndStart());
    std::cout << ">>>> Server listening on " << server_address << std::endl;
    server->Wait();
}

int main(int argc, char** argv) {

    std::string ip = "{{ ip }}";
    std::string port = "{{ port }}";
    std::string broadcastAddress = "{{ broadcast_address }}";  // Default broadcast addressIP "192.168.0.255"
    std::string broadcastPort = "{{ broadcast_port }}";     // Default broadcast port

    std::cout << ">>>>[INFO] Server IP: " << ip << std::endl;
    std::cout << ">>>>[INFO] Server Port: " << port << std::endl;
    std::cout << ">>>>[INFO] Broadcast Address: " << broadcastAddress << std::endl;
    std::cout << ">>>>[INFO] Broadcast Port: " << broadcastPort << std::endl;

    std::thread senderThread(monitorComm, broadcastAddress, std::stoi(broadcastPort));
    // TODO: Add other threads here
    
    RunServer(ip,port);
    
    senderThread.join();

    return 0;
}